﻿using Serilog;
using Serilog.Context;
using Serilog.Events;
using System.Diagnostics;

namespace HDW_Migrate.Extensions;

/// <summary>
/// 日志扩展
/// </summary>
static class LoggerEx
{
    /// <summary>
    /// 增加日志控制
    /// </summary>
    /// <param name="builder"></param>
    public static void AddLogging(this WebApplicationBuilder builder)
    {
        Log.Logger = new LoggerConfiguration().MinimumLevel
            .Information()
            // 过滤掉不必要的 ASP.NET Core 内部日志
            .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
            .MinimumLevel.Override("System", LogEventLevel.Warning)
            .MinimumLevel.Override("Microsoft.AspNetCore.Hosting.Diagnostics", LogEventLevel.Warning)
            .MinimumLevel.Override("Microsoft.AspNetCore.Routing", LogEventLevel.Warning)
            .MinimumLevel.Override("Microsoft.AspNetCore.Mvc", LogEventLevel.Warning)
            // 保留程序生命周期日志
            .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information)
            // 控制台日志
            .WriteTo.Console(
                outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
            )
            // 主日志文件 - 包含所有信息级别以上的日志（排除异常）
            .WriteTo.File(
                path: "../logs/app-.log",
                rollingInterval: RollingInterval.Day,
                retainedFileCountLimit: 30,
                fileSizeLimitBytes: 10 * 1024 * 1024,
                outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
                restrictedToMinimumLevel: LogEventLevel.Information
            )
            // 异常日志文件 - 只记录错误和致命级别的日志，且独立存储
            .WriteTo.Logger(
                lc =>
                    lc.Filter
                        .ByIncludingOnly(evt => evt.Level >= LogEventLevel.Error)
                        .WriteTo.File(
                            path: "../logs/errors-.log",
                            rollingInterval: RollingInterval.Day,
                            retainedFileCountLimit: 30,
                            //outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}"
                            outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] {Message:lj}{NewLine}{Exception}{NewLine}"
                        )
            )
            .Enrich.FromLogContext()
            .CreateLogger();

        // 使用 Serilog
        builder.Host.UseSerilog();
    }

    /// <summary>
    /// 使用自定义请求日志中间件
    /// </summary>
    /// <param name="app"></param>
    /// <returns></returns>
    public static IApplicationBuilder UseLogging(this IApplicationBuilder app)
    {
        return app.UseMiddleware<RequestLoggingMiddleware>();
    }
}

/// <summary>
/// 日志中间件
/// </summary>
/// <param name="next"></param>
/// <param name="logger"></param>
class RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
{
    /// <summary>
    /// 请求时调用
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public async Task InvokeAsync(HttpContext context)
    {
        var sw = Stopwatch.StartNew();
        try
        {
            await next(context);
            sw.Stop();

            // 只有成功请求才记录完成信息
            // 通过检查是否已经有异常处理标记来避免重复记录
            if (!context.Items.ContainsKey("ExceptionHandled"))
            {
                using (LogContext.PushProperty("RequestPath", context.Request.Path))
                using (LogContext.PushProperty("RequestMethod", context.Request.Method))
                using (LogContext.PushProperty("StatusCode", context.Response.StatusCode))
                using (LogContext.PushProperty("ElapsedMs", sw.ElapsedMilliseconds))
                {
                    var level =
                        context.Response.StatusCode > 499
                            ? LogEventLevel.Error
                            : context.Response.StatusCode > 399
                                ? LogEventLevel.Warning
                                : LogEventLevel.Information;

                    logger.LogInformation(
                        "{RequestMethod} {RequestPath} {StatusCode} in {ElapsedMs}ms",
                        context.Request.Method,
                        context.Request.Path,
                        context.Response.StatusCode,
                        sw.ElapsedMilliseconds
                    );
                }
            }
        }
        catch (Exception)
        {
            sw.Stop();

            // 标记异常已被处理，避免重复记录
            context.Items["ExceptionHandled"] = true;

            // 异常已经在全局异常处理中间件中记录了，这里不再重复记录
            // 只记录请求失败的信息（不含异常详情）
            using (LogContext.PushProperty("RequestPath", context.Request.Path))
            using (LogContext.PushProperty("RequestMethod", context.Request.Method))
            {
                logger.LogError(
                    "{RequestMethod} {RequestPath} in {ElapsedMs}ms",
                    context.Request.Method,
                    context.Request.Path,
                    sw.ElapsedMilliseconds
                );
            }

            throw;
        }
    }
}